package server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.*;

public class Bootstrap {

    private static final String WEB_FILE_NAME = "web.xml";

    private Host host = new Host();

    /**
     * 程序启动入口
     *
     * -----------tomcat体系结构在项目Tomcat-architecture.md中
     *
     * 1.新建LagouServlet1和LagouServlet2，启动容器，找到target下的class文件，分别复制进入
     *      /Users/test/webapps/demo1/server/LagouServlet1.class
     *      /Users/test/webapps/demo2/server/LagouServlet2.class
     *      本地绝对路径，模拟项目demo1和demo2已经完成编译
     *
     * 2.新建server.xml，配置绝对路径/Users/test/webapps和端口等信息
     *
     * 3.加载Minicat的server.xml，读取webapp路径和端口
     *
     * 4.定义Host，Mapper类：Mapper存储url和class映射关系，代替原来的map，Host存储端口，webapp，和List<Mapper>
     *     启动的时候，加载配置，设置对象Host的值，webXml()中给List<Mapper>赋值
     *
     * 5.定义loadWebApp方法，主要作用是获取webapp下所有的web.xml，因为web.xml中配置了servlet的位置，所以只需要找
     *      到webapp下所有的web.xml即可，并放入List<Mapper>中，此方法中递归查询所有web.xml,获取到web.xml，生成
     *      FileInputStream,读取web.xml中配置的servlet，生成实例，存储List<Mapper>中，等待匹配url，拿到servlet
     *      实例，调用doGet或者doPost方法。
     *
     * 6.请求示例：
     *      * http://localhost:8080/lagou1
     *      * http://localhost:8080/lagou2
     *
     * @param args
     */
    public static void main(String[] args) {
        try {
            new Bootstrap().start();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void start() throws Exception {
        // 加载解析相关的配置，web.xml
        loadServlet();
        // 定义一个线程池
        int corePoolSize = 10;
        int maximumPoolSize = 50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
        ThreadPoolExecutor threadPoolExecutor =
                new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit,
                        workQueue, threadFactory, handler);
        int port = host.getPort();
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("=====>>>Minicat start on port：" + port);
        System.out.println("=========>>>>>>使用线程池进行多线程改造");
        /*
            多线程改造（使用线程池）
         */
        while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, host.getMapperList());
            threadPoolExecutor.execute(requestProcessor);
        }
    }

    /**
     * 加载解析web.xml，初始化Servlet
     */
    private void loadServlet() {
        InputStream serverResourceAsStream =
                this.getClass().getClassLoader().getResourceAsStream("server.xml");
        serverXml(serverResourceAsStream);
        loadWebApp();
        System.out.println();
    }

    private void loadWebApp() {
        selectFile(new File(host.getAppBase()));
    }

    private void selectFile(File file) {
        try {
            File[] files = file.listFiles();
            for (File listFile : files) {
                if (listFile.isDirectory()) {
                    selectFile(listFile);
                }
                if (listFile.isFile() && WEB_FILE_NAME.equals(listFile.getName())) {
                    String path = listFile.getPath();
                    File xmlPathFile = new File(path);
                    FileInputStream fileInputStream = new FileInputStream(xmlPathFile);
                    webXml(fileInputStream);
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    private void serverXml(InputStream serverResourceAsStream) {
        try {
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(serverResourceAsStream);
            Element rootElement = document.getRootElement();
            Element connectorElement = (Element) rootElement.selectSingleNode("//Connector");
            String s = connectorElement.attribute("port").getValue();
            Integer port = Integer.parseInt(s);
            Element hostElement = (Element) rootElement.selectSingleNode("//Host");
            String name = hostElement.attribute("name").getValue();
            String appBase = hostElement.attribute("appBase").getValue();
            host.setPort(port);
            host.setName(name);
            host.setAppBase(appBase);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    private void webXml(InputStream webResourceAsStream) {
        try {
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(webResourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (int i = 0; i < selectNodes.size(); i++) {
                Element element = selectNodes.get(i);
                // <servlet-name>lagou</servlet-name>
                Element servletnameElement = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletnameElement.getStringValue();
                // <servlet-class>server.LagouServlet</servlet-class>
                Element servletclassElement = (Element) element.selectSingleNode("servlet-class");
                String servletClass = servletclassElement.getStringValue();
                // 根据servlet-name的值找到url-pattern
                Element servletMapping = (Element) rootElement.selectSingleNode(
                        "/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                List<Mapper> mapperList = host.getMapperList();
                Mapper mapper = new Mapper();
                mapper.setUrlPattern(urlPattern);
                mapper.setHttpServlet((HttpServlet) Class.forName(servletClass).newInstance());
                mapperList.add(mapper);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

}
